/** Fichier définissant un icosaèdre.
 *
 * Ce fichier contient la définition de la classe Icosaedre.
 *
 * @file Icosaedre.hpp
 * @author Johan "Solo" Luisier
 * @date 2010
 */
#include "Icosaedre.hpp"

#include "Algebre/ConstruitMatrice.hpp"

#include "Utils/StatusCode.hpp"
#include "Utils/debugstreams.hpp"
#include "Utils/Erreur.hpp"

#include <cmath>

#include <boost/foreach.hpp>

using namespace std;

const double Icosaedre::Phi( ( 1. + sqrt( 5 ) ) / 2. );

/** Constructeur standard.
 *
 * Ce constructeur initialise les 20 faces d'un Icosaedre. Le principe est
 * d'utiliser les 20 sommets d'un dodécaèdre.
 *
 * Ici on itinialise la transformation qui permet de passer des coordonnèes
 * locales (utilisées pour définir les sommets) et les coordonéées globales,
 * utilisées pour les Plans.
 *
 * @thrwo Erreur si les vecteurs ne sont pas orthogonaux.
 *
 * @param[in] centre centre de gravité de l'Icosaedre.
 * @param[in] axeX direction de 0x' dans le référentiel global.
 * @param[in] axeY direction de 0y' dans le référentiel global.
 * @param[in] ratio rapport d'homothécie. Pour un ratio de 1., l'Icosaedre
 * peut être inscrit dans un cube de côté 1.
 */
Icosaedre::Icosaedre(const Coord& centre, const VecteurU& axeX,
                     const VecteurU& axeY, const double& ratio)
    : Centre( centre )//, AxeX( axeX ), AxeY( axeY ), Homothetie( ratio )
{
    MatriceSO3 test;

    StatusCode sc( axesXY( axeX, axeY, test ) );

    if ( ! sc )
    {
        error() << sc.message() << endreq;
        exit( 1 );
    }
    else
    {
        VersGlobal = Transformation( test, Centre, ratio / ( 2. * Phi ), false );
        VersLocal  = VersGlobal.inverse();
    }

    /*
   * On définit premièrement tous les points dans le repère local.
   */

    Coord yz( 0., 1. / Phi, Phi );
    Coord xy( 1. / Phi, Phi, 0. );
    Coord xz( Phi, 0., 1. / Phi );
    Coord autre( 1., 1., 1.);

    vector< Coord > sommetsLoc, sommetsGlob;
    unsigned int i, j(0);

    for (i = 0; i < 4; i++, j++)
        sommetsLoc.push_back( symetries( yz, i ) );

    for (i = 2; i < 6; i++, j++)
        sommetsLoc.push_back( symetries( xz, i ) );

    for (i = 1; i < 3; i++, j++)
        sommetsLoc.push_back( symetries( xy, i ) );
    for (i = 5; i < 7; i++, j++)
        sommetsLoc.push_back( symetries( xy, i ) );

    for (i = 0; i < 8; i++, j++)
        sommetsLoc.push_back( symetries( autre, i ) );

    sommetsGlob.resize( sommetsLoc.size() );
    transform( sommetsLoc.begin(), sommetsLoc.end(), sommetsGlob.begin(),
               VersGlobal );
    
    BOOST_FOREACH( Coord coord, sommetsGlob)
            listplan.push_back( Plan::SharedPtr( new Plan( Vecteur( Centre, coord ),
                                                           coord ) ) );

    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listplan[0].get(),
                                                                   listplan[1].get() ) ) );
    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listplan[2].get(),
                                                                   listplan[3].get() ) ) );
    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listplan[4].get(),
                                                                   listplan[5].get() ) ) );
    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listplan[6].get(),
                                                                   listplan[7].get() ) ) );
    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listplan[8].get(),
                                                                   listplan[9].get() ) ) );
    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listplan[10].get(),
                                                                   listplan[11].get() ) ) );
    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listplan[12].get(),
                                                                   listplan[13].get() ) ) );
    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listplan[14].get(),
                                                                   listplan[15].get() ) ) );
    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listplan[16].get(),
                                                                   listplan[17].get() ) ) );
    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listplan[18].get(),
                                                                   listplan[19].get() ) ) );

    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listFC[0].get(),
                                                                   listFC[1].get() ) ) );
    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listFC[2].get(),
                                                                   listFC[3].get() ) ) );
    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listFC[4].get(),
                                                                   listFC[5].get() ) ) );
    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listFC[6].get(),
                                                                   listFC[7].get() ) ) );
    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listFC[8].get(),
                                                                   listFC[9].get() ) ) );

    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listFC[10].get(),
                                                                   listFC[11].get() ) ) );
    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listFC[12].get(),
                                                                   listFC[13].get() ) ) );

    listFC.push_back( FormeComposee::SharedPtr( new FormeComposee( inter, listFC[15].get(),
                                                                   listFC[16].get() ) ) );

    cerr << "# FC : " << listFC.size() << endl;

    form[0] = listFC[14].get();
    form[1] = listFC[17].get();

    op = inter;

    Nom = "Icosaèdre";
    PseudoCentre = Centre;
    PseudoRayon = ratio;
}

/**
 * Le destructeur n'a rien de spécial à faire.
 */
Icosaedre::~Icosaedre()
{}

/** Permet d'utiliser les symétries du solide pour générer la liste des sommets.
 *
 * Les sommets de lu dodécaèdre répondent à une liste d'opérations de symétrie,
 * qui sont utilisées ici pour générer l'ensemble des sommets à partir des 4
 * sommets de départ.
 *
 * @param[in] pt point correspondant à l'identité.
 * @param[in] i "numéro" de la symétrie à appliquer : 1 signifie que l'on
 * inverse la coordonnée z, 2 la coordonnée y. En fait le nombre, vu
 * en binaire, dit quels sont les inversions effectuées.
 *
 * @return l'image de pt une fois la symétrie voulue appliquée.
 */
Coord Icosaedre::symetries(const Coord& pt, const unsigned int& i) const
{
    Coord tmp( pt );

    if ( i % 2 == 1 && abs( tmp.z() ) > 1.e-7 )
        tmp.setZ( - tmp.z() );

    if ( ( i % 4 == 2 || i % 4 == 3 ) && abs( tmp.y() ) > 1.e-7 )
        tmp.setY( - tmp.y() );

    if ( i / 4 == 1 && abs( tmp.x() ) > 1.e-7 )
        tmp.setX( - tmp.x() );

    return tmp;
}
